﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using WooCoo.Tools;

namespace WooCoo.DataPortal.Client
{
    /// <summary>
    /// 提供对数据门户的查询处理类
    /// </summary>
    /// <typeparam name="TElement"></typeparam>
    public class DataPortalQuery<TElement> : DataPortalQuery, IQueryable<TElement>, IEnumerable<TElement>, IQueryable, IEnumerable
    {

        private static readonly MethodInfo expandMethodInfo = typeof(DataPortalQuery<TElement>).GetMethod("Expand", new Type[]
		{
			typeof(string)
		});
        private static readonly MethodInfo expandGenericMethodInfo = (MethodInfo)typeof(DataPortalQuery<TElement>).GetMember("Expand*").Single((MemberInfo m) => ((MethodInfo)m).GetGenericArguments().Count<Type>() == 1);
        private readonly Expression queryExpression;
        private readonly DataPortalQueryProvider queryProvider;
        private QueryComponents queryComponents;
        public override Type ElementType
        {
            get
            {
                return typeof(TElement);
            }
        }
        public override Expression Expression
        {
            get
            {
                return this.queryExpression;
            }
        }
        public override IQueryProvider Provider
        {
            get
            {
                return this.queryProvider;
            }
        }
        /// <summary>
        /// 对查询条件分析后得到的请求地址
        /// </summary>
        public Uri RequestUri
        {
            get
            {
                return this.Translate().Uri;
            }
            internal set
            {
                this.Translate().Uri = value;
            }
        }
        /// <summary>The ProjectionPlan for the request (if precompiled in a previous page).</summary>
        //internal override ProjectionPlan Plan
        //{
        //    get
        //    {
        //        return null;
        //    }
        //}
        private DataPortalQuery(Expression expression, DataPortalQueryProvider provider)
        {
            this.queryExpression = expression;
            this.queryProvider = provider;
        }
        public new IAsyncResult BeginExecute(AsyncCallback callback, object state)
        {
            //return base.BeginExecute(this, this.queryProvider.Proxy, callback, state, "Execute");
            throw new NotImplementedException();
        }
        public new IEnumerable<TElement> EndExecute(IAsyncResult asyncResult)
        {
            //return DataServiceRequest.EndExecute<TElement>(this, this.queryProvider.Context, "Execute", asyncResult);
            throw new NotImplementedException();
        }
        /// <summary>
        /// Returns an IEnumerable from an Internet resource. 
        /// </summary>
        /// <returns>An IEnumerable that contains the response from the Internet resource.</returns>
        public new IEnumerable<TElement> Execute()
        {
            //return base.Execute<TElement>(this.queryProvider.Context, this.Translate());
            return this.queryProvider.Proxy.Fetch<TElement>(this.Translate()).ReturnObject as IEnumerable<TElement>;
        }
        /// <summary>
        /// Sets Expand option
        /// </summary>
        /// <param name="path">Path to expand</param>
        /// <returns>New DataServiceQuery with expand option</returns>
        public DataPortalQuery<TElement> Expand(string path)
        {
            Check.CheckArgumentNullAndEmpty(path, "path");
            return (DataPortalQuery<TElement>)this.Provider.CreateQuery<TElement>(
                Expression.Call(
                    Expression.Convert(this.Expression, typeof(DataPortalQuery<TElement>.DataPortalOrderedQuery)),
                    DataPortalQuery<TElement>.expandMethodInfo,
                    new Expression[] { Expression.Constant(path) }));
        }
        public DataPortalQuery<TElement> Expand<TTarget>(Expression<Func<TElement, TTarget>> navigationPropertyAccessor)
        {
            Check.CheckArgumentNull<Expression<Func<TElement, TTarget>>>(navigationPropertyAccessor, "navigationPropertyAccessor");

            MethodInfo mi = DataPortalQuery<TElement>.expandGenericMethodInfo.MakeGenericMethod(typeof(TTarget));
            return (DataPortalQuery<TElement>)this.Provider.CreateQuery<TElement>(
                Expression.Call(
                    Expression.Convert(this.Expression, typeof(DataPortalQuery<TElement>.DataPortalOrderedQuery)),
                    mi,
                    new Expression[] { navigationPropertyAccessor }));
        }
        /// <summary>Requests that the count of all entities in the entity set be returned inline with the query results.</summary>
        /// <returns>A new <see cref="T:Microsoft.OData.Client.DataServiceQuery`1" /> object that has the inline count option set.</returns>
        public DataPortalQuery<TElement> IncludeTotalCount()
        {
            MethodInfo method = typeof(DataPortalQuery<TElement>).GetMethod("IncludeTotalCount");
            return (DataPortalQuery<TElement>)this.Provider.CreateQuery<TElement>(
                Expression.Call(
                Expression.Convert(this.Expression, typeof(DataPortalQuery<TElement>.DataPortalOrderedQuery)), 
                method));
        }
        /// <summary>
        /// 设定一个自定义扩展选项
        /// </summary>
        /// <param name="name">选项名称</param>
        /// <param name="value">选项值</param>
        /// <returns>附有扩展选项的DataPortalQuery对象</returns>
        public DataPortalQuery<TElement> AddQueryOption(string name, object value)
        {
            Check.CheckArgumentNull<string>(name, "name");
            Check.CheckArgumentNull<object>(value, "value");
            MethodInfo method = typeof(DataPortalQuery<TElement>).GetMethod("AddQueryOption");
            return (DataPortalQuery<TElement>)this.Provider.CreateQuery<TElement>(Expression.Call(Expression.Convert(this.Expression, typeof(DataPortalQuery<TElement>.DataPortalOrderedQuery)), method, new Expression[]
			{
				Expression.Constant(name), 
				Expression.Constant(value, typeof(object))
			}));
        }
        /// <summary>
        /// get an enumerator materializes the objects the Uri request
        /// </summary>
        /// <returns>an enumerator</returns>
        public IEnumerator<TElement> GetEnumerator()
        {
            return this.Execute().GetEnumerator();
        }
        /// <summary>
        /// 返回对应的请求地址
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            string result;
            try
            {
                result = this.QueryComponents().Uri.ToString();
            }
            catch (NotSupportedException ex)
            {
                //result = Strings.ALinq_TranslationError(ex.Message);
                throw Error.NotSupported(StringID.TranslationError.ToLocalStringFormat(ex.Message));
            }
            return result;
        }
        /// <summary>
        /// get an enumerator materializes the objects the Uri request
        /// </summary>
        /// <returns>an enumerator</returns>
        IEnumerator IEnumerable.GetEnumerator()
        {
            return this.GetEnumerator();
        }
        internal QueryComponents QueryComponents()
        {
            return this.Translate();
        }
        internal override IEnumerable ExecuteInternal()
        {
            return this.Execute();
        }
        internal override IAsyncResult BeginExecuteInternal(AsyncCallback callback, object state)
        {
            return this.BeginExecute(callback, state);
        }
        internal override IEnumerable EndExecuteInternal(IAsyncResult asyncResult)
        {
            return this.EndExecute(asyncResult);
        }
        /// <summary>
        /// 将表达式进行转换，获得查询组件
        /// </summary>
        /// <returns>查询组件</returns>
        private QueryComponents Translate()
        {
            if (this.queryComponents == null)
            {
                this.queryComponents = this.queryProvider.Proxy.Translate(this.queryExpression);
            }
            return this.queryComponents;
        }

        /// <summary>
        /// Synchronizely get the query set count from the server by executing the $count=value query
        /// </summary>
        /// <param name="context">The context</param>
        /// <returns>The server side count of the query set</returns>
        internal long GetQuerySetCount(IDataPortalProxy context)
        {
            return 0;
            //Debug.Assert(null != context, "context is null");
            //this.QueryComponents.Version = Util.DataServiceVersion2;

            //QueryResult response = null;
            //DataServiceRequest<long> serviceRequest = new DataServiceRequest<long>(this.QueryComponents, null);
            //HttpWebRequest request = context.CreateRequest(this.QueryComponents.Uri, XmlConstants.HttpMethodGet, false, null, this.QueryComponents.Version, false);

            //request.Accept = "text/plain";
            //response = new QueryResult(this, "Execute", serviceRequest, request, null, null);

            //try
            //{
            //    response.Execute();

            //    if (HttpStatusCode.NoContent != response.StatusCode)
            //    {
            //        StreamReader sr = new StreamReader(response.GetResponseStream());
            //        long r = -1;
            //        try
            //        {
            //            r = XmlConvert.ToInt64(sr.ReadToEnd());
            //        }
            //        finally
            //        {
            //            sr.Close();
            //        }

            //        return r;
            //    }
            //    else
            //    {
            //        throw new DataServiceQueryException(Strings.DataServiceRequest_FailGetCount, response.Failure);
            //    }
            //}
            //catch (InvalidOperationException ex)
            //{
            //    QueryOperationResponse operationResponse = null;
            //    operationResponse = response.GetResponse<long>(MaterializeAtom.EmptyResults);
            //    if (null != operationResponse)
            //    {
            //        operationResponse.Error = ex;
            //        throw new DataServiceQueryException(Strings.DataServiceException_GeneralError, ex, operationResponse);
            //    }

            //    throw;
            //}
        }
        /// <summary>
        /// 构造方法
        /// </summary>
        /// <param name="expression">查询表达式</param>
        /// <param name="provider">数据门户查询提供程序</param>
        internal class DataPortalOrderedQuery : DataPortalQuery<TElement>, IOrderedQueryable<TElement>, IQueryable<TElement>, IEnumerable<TElement>, IOrderedQueryable, IQueryable, IEnumerable
        {
            internal DataPortalOrderedQuery(Expression expression, DataPortalQueryProvider provider)
                : base(expression, provider)
            {
            }
        }
    }

}
